/**
 * 配置
 * */
package org.zmhhxl.sample3.oauth2.a.configure;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SimpleSavedRequest;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsConfigurationSource;
import org.zmhhxl.api.auth.RemoteThirdPartyService;
import org.zmhhxl.api.auth.RemoteUserInfoService;
import org.zmhhxl.sample3.oauth2.a.filter.CookieCsrfFilter;
import org.zmhhxl.sample3.oauth2.a.handler.LoginSuccessHandler;
import org.zmhhxl.sample3.oauth2.a.handler.OAuth2UserRepositoryHandler;
import org.zmhhxl.sample3.oauth2.a.handler.OidcUserRepositoryHandler;
import org.zmhhxl.sample3.oauth2.a.service.CustomOAuth2UserService;
import org.zmhhxl.sample3.oauth2.a.service.CustomOidcUserInfoService;
import org.zmhhxl.sample3.oauth2.a.service.CustomUserDetailsService;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Joe Grandja
 * @author Steve Riesenberg
 * @since 1.1
 */
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)  //proxyBeanMethods = false
@Slf4j
public class DefaultSecurityConfig {
   // :/favicon.ico,/login/**,/test/**,/resources/**,/error,/webjars/**
//   @Value("${spring.security.permitAllPaths}")
//   private String[] permitAllPaths;

      // 不需要认证即可访问的路径 可以赋初始值
      @Value("${custom.security.permit-all-paths}")
      private String permitAllPaths;

   /**
    * 不需要认证即可访问的路径
    */
//   @Autowired
//   private /*final*/ CustomSecurityProperties customSecurityProperties ;

         @Autowired
         private CustomUserDetailsService customUserDetailsService ;
         //@Autowired
         //private final UserDetailsService cachingUserDetailsService = new CachingUserDetailsService(customUserDetailsService);
         @Autowired
         private CustomOAuth2UserService customOAuth2UserService;
         @Autowired
         private CustomOidcUserInfoService customOidcUserService;

         @Autowired
         private  CorsConfigurationSource corsConfigurationSource;

         @Autowired
         private RememberMeServices rememberMeServices;

   @Bean
   public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, RemoteThirdPartyService thirdPartyUserService, RemoteUserInfoService userInfoService) throws Exception {

      // 将配置项中的URL模式转换为请求匹配器
      List<RequestMatcher> requestMatchers = Arrays.stream(permitAllPaths.split(","))
            .map(String::trim)
            .map(pattern -> new AntPathRequestMatcher(pattern))
            .collect(Collectors.toList());

      //todo: zmhhxl 2024-6-5 : 配置跨域有待完善安全问题
      http.headers(headers -> headers
//                  .disable()    //忽略全部头安全
                  .frameOptions(frameOptions -> frameOptions.sameOrigin()) //.disable() .sameOrigin()

//                  .contentSecurityPolicy(contentSecurityPolicy -> contentSecurityPolicy
//                  .policyDirectives(  //'self',frame-ancestors '*','http://localhost:4200','http://127.0.0.1:9000','http://127.0.0.1:8080';
//                        "sandbox allow-forms allow-scripts; frame-ancestors 'self' 127.0.0.1:4200 127.0.0.1:9000; "  //none  child-src 'self';
//                  )
//            )

      );

      // AbstractHttpConfigurer::disable
      http.csrf(csrf -> csrf
            .ignoringRequestMatchers("http://127.0.0.1:4200/silent-refresh.html")  //禁用CSRF仅为某些路径
                       .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                       .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())

      ).addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class);

      http
               .authorizeHttpRequests(authorize -> authorize
                     // 放行的路径 assets/**, /favicon.ico, /resources/**, /webjars/**, /actuator/**, /swagger-ui/**, /error, /logs/**, /oauth2/jwks, /oauth2/authorize/*, /test/**, /.well-known/*, /login, /logged-out
                     //.requestMatchers( "assets/**", "/favicon.ico", "/resources/**", "/webjars/**", "/actuator/**", "/swagger-ui/**", "/error", "/logs/**", "/oauth2/jwks", "/oauth2/authorize/*", "/test/**", "/.well-known/*", "/login", "/logged-out").permitAll()
                     //.requestMatchers(customSecurityProperties.getAllowedUrlsOrigin().stream().collect(Collectors.joining(","))).permitAll()
                     .requestMatchers(requestMatchers.toArray(new RequestMatcher[0])).permitAll()
                     .requestMatchers(HttpMethod.OPTIONS, "/.well-known/*").permitAll()  // 放行的操作 "/**" openid-configuration
                     .requestMatchers(HttpMethod.OPTIONS, "/oauth2/jwks").permitAll()  // "/**"
                     .requestMatchers(HttpMethod.OPTIONS, "/oauth2/authorize").permitAll()  // "/**"
                     .requestMatchers(HttpMethod.OPTIONS, "/session-check").permitAll()
                     .requestMatchers(HttpMethod.OPTIONS, "/oauth2/check_session_iframe").permitAll()
                     .requestMatchers(HttpMethod.OPTIONS, "/userinfo").permitAll()
                     .requestMatchers(HttpMethod.OPTIONS, "/oauth2/token").permitAll()
                     .requestMatchers(HttpMethod.OPTIONS, "/login/**").permitAll()
                     .anyRequest().authenticated())

               .formLogin((formLogin)->{
                      log.info("缺省安全配置 bbbbb");    // 启动时已经调用过了->ooooo->oidc认证成功 sssss
                        formLogin.loginPage("/login")   // 配置自定义的登录界面
                              .loginProcessingUrl("/login")   // 提交active=登录请求的URL
                              //密码本地登陆成功后的处理
                              .successHandler(authenticationSuccessHandler(thirdPartyUserService, userInfoService ))  // 优先处理customUserDetailsService
                              //.defaultSuccessUrl("http://127.0.0.1:4200/index.html", true)
                              //.failureHandler(new LoginFailureHandler())
                              //.failureUrl("/login?error=true")
                              .permitAll()
                              ;

//                        // 登录后处理
//                        formLogin.loginProcessingUrl("http://127.0.0.1:9000/login");

                        //Customizer.withDefaults();   // TODO 使用默认配置，一切正常???
                     }
               )  //end formLogin

             // 配置 CORS,指定不同的 CorsConfigurationSource
             //.cors(Customizer.withDefaults())
             .cors(cors ->cors.configurationSource(corsConfigurationSource))
            //.cors().configurationSource(corsConfigurationSource)
//             .userDetailsService(userDetailsService())

                // // 作为客户端去登陆其他网站, // 这是联合身份认证
               .oauth2Login(
                     oauth2-> {
                        log.info("缺省安全配置 bbbbb ooooo");   // 只在启动时调用一次
                      oauth2.loginPage("/login").permitAll()  ////配置自定义的登录界面
                            .successHandler(authenticationSuccessHandler(thirdPartyUserService, userInfoService))
                      ;
                      // 配置 OIDC userinfo
                      oauth2.userInfoEndpoint(userInfoEndpoint->{
                                           userInfoEndpoint.userService(customOAuth2UserService);
                                           userInfoEndpoint.oidcUserService(customOidcUserService);
                      });
////                        oauth2.loginProcessingUrl("http://127.0.0.1:4200/callback");
////                         // 配置 OIDC userinfo  todo 待完善 redis缓存
//                       oauth2.userInfoEndpoint( userInfo->{
//                           // userInfo.oidcUserService(customOidcUserService);
//                           //userInfo.userAuthoritiesMapper(); customUserInfoService::save
//                          userInfo.userService(customOAuth2UserService);  //设置用于从UserInfo端点获取最终用户的用户属性的OAuth 2.0服务。
//                          userInfo.oidcUserService();   //设置用于从UserInfo端点获取最终用户的用户属性的OpenID Connect 1.0服务。
//                          userInfo.userAuthoritiesMapper();  //设置用于映射oauth2user.getauthority()的grantedauthoritmapper。 映射接口可以被注入到认证层,以将从存储加载到身份验证对象中使用的权限转换成文件。
//                           })
//                           ;

//
//                           Customizer.withDefaults()
                       }
               )

             // 若是使用第三方，则注入即可,// 配置用户详情实现类 ::loadUserByUsername  cachingUserDetailsService
            .userDetailsService(customUserDetailsService)    // 注入自定义的UserDetailsService,先于登录成功处理

            // 登出配置
            .logout(logout->{
                     logout.logoutUrl("/connect/logout")
                          .logoutSuccessUrl("/connect/logout")
                          .deleteCookies("JSESSIONID")
                          .invalidateHttpSession(true)
                          .clearAuthentication(true)
                          .permitAll();
               //Customizer.withDefaults();
            })

            //记住我配置
            .rememberMe(rememberMe ->{
               new RememberMeConfig();
            })   // rememberMe()
       ;
       return http.build();
   }

   // @formatter:off

   private AuthenticationSuccessHandler authenticationSuccessHandler(RemoteThirdPartyService thirdPartyUserService, RemoteUserInfoService userInfoService) {
      //配置时执行一次
      log.info("oidc认证成功 sssss ==============");  //.setOauth2UserHandler(new OAuth2UserRepositoryHandler())
      LoginSuccessHandler loginSuccessHandler = new LoginSuccessHandler(/*thirdPartyUserService, userInfoService*/);
      loginSuccessHandler.setOauth2UserHandler(new OAuth2UserRepositoryHandler(userInfoService,thirdPartyUserService));
      loginSuccessHandler.setOidcUserHandler(new OidcUserRepositoryHandler());
      return loginSuccessHandler;
//      return (request, response, authentication) -> {
//         response.setStatus(200)
//         //DaoAuthenticationProvider
//         //AuthorizationGrantType.CLIENT_CREDENTIALS
//      };
   }

   /**
    * 配置一个自定义的RequestCache bean，用于存储未处理的请求。
    * 这个实现专注于使用HTTP Referer头作为保存的请求URL。
    * 如果Referer头不可用，则使用请求的URL。
    */
   @Bean
   public RequestCache refererRequestCache() {
      return new HttpSessionRequestCache() {

         /**
          * 当请求需要被缓存时，此方法被调用。
          * 它首先尝试从HTTP Referer头获取请求的URL。
          * 如果Referer头不存在，则使用请求的URL。
          * 最后，将保存的请求信息存储到会话中。
          *
          * @param request 当前的HTTP请求
          * @param response 当前的HTTP响应
          */
         @Override
         public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
            // 尝试从HTTP头中获取Referer
            String referrer = request.getHeader("referer");
            // 如果Referer不存在，则使用请求的URL作为来源
            if (referrer == null) {
               referrer = request.getRequestURL().toString();
            }
            // 将请求的URL作为保存的请求信息存储到会话中
            request.getSession().setAttribute("SPRING_SECURITY_SAVED_REQUEST",
                  new SimpleSavedRequest(referrer));

         }
      };
   }

//   @Bean
//   public UserDetailsService cachingUserDetailsService(UserDetailsService userDetailsService) {
//      return new CachingUserDetailsService(userDetailsService);
//   }

//   @Bean
//   public UserDetailsService userDetailsService() {
//      UserDetails user = User.withDefaultPasswordEncoder()
//            .username("user1")
//            .password("p")
//            .roles(new String[]{"USER", "ADMIN1"})   //放入authorities
//            .build();
//      return new InMemoryUserDetailsManager(user);
//   }
   // @formatter:on

   // 配置自定义 oidc userinfo
//   @Bean
//   public OAuth2UserService<OidcUserRequest, OidcUser> customOidcUserService() {
//      OidcUserService delegate = new OidcUserService();
//      return new CustomOidcUserService(delegate);
//   }


   @Bean
   public SessionRegistry sessionRegistry() {
      return new SessionRegistryImpl();
   }

   @Bean
   public HttpSessionEventPublisher httpSessionEventPublisher() {
      return new HttpSessionEventPublisher();
   }

//   @Bean
//   public PlatformTransactionManager transactionManager(DataSource dataSource) {
//      JpaTransactionManager transactionManager = new JpaTransactionManager();
//      transactionManager.setEntityManagerFactory(entityManagerFactory);
//      return transactionManager;
//   }
}
